// 双向绑定
// 在初始化挂载时进行监听，存储updata函数
import { pushTarget, popTarget } from "./dep"
import { nextTick } from '../utils/nextTick'
let id = 0
class watcher {
    constructor(vm, updataComponent, cb, options) {
        this.vm = vm
        this.exprOrfn = updataComponent
        this.cb = cb
        this.options = options
        this.id = id++
        this.user = !!options.user
        this.deps = []
        this.depsId = new Set()
        // computed
        this.lazy = !!options.lazy
        this.dirty = this.lazy // 默认不计算

        if (typeof updataComponent === 'function') {
            this.getter = updataComponent
        } else {
            // 处理对象属性的watch监听 a.b.c
            this.getter = () => {
                let path = this.exprOrfn.split('.')
                let obj = vm
                for (let i = 0; i < path.length; i++) {
                    obj = obj[path[i]]
                }
                return obj
            }
        }
        this.value = this.lazy ? void 0 : this.get() // 默认不计算
    }
    // 存储dep 多对多
    addDep(dep) {
        let id = dep.id
        if (!this.depsId.has(id)) {
            this.deps.push(dep)
            this.depsId.add(id)
            dep.addSub(this)
        }
    }
    // 初始化
    get() {
        pushTarget(this) // 添加watcher
        const value = this.getter.call(this.vm)
        popTarget() // 去掉watcher
        return value
    }
    // 更新
    updata() {
        if (this.lazy) { // 重新计算computed
            this.dirty = true
        } else {
            queueWtacher(this)
        }
    }
    run() {
        const value = this.get()
        const oldValue = this.value
        this.value = value
        if (this.user) {
            this.cb.call(this.vm, value, oldValue)
        }
    }
    evaluate() {
        this.value = this.get()
        this.dirty = false
    }
    depend() {
        let i = this.deps.length
        while (i--) {
            this.deps[i].depend()
        }
    }
}

let queue = []
let has = {}
let pending = false
function flushWatcher() {
    queue.forEach(item => {
        item.run()
        if (!item.user) {
            item.cb()
        }
    })
    queue = []
    has = {}
    pending = false
}
function queueWtacher(watcher) {
    const id = watcher.id
    if (!has[id]) {
        queue.push(watcher)
        has[id] = true
        if (!pending) {
            nextTick(flushWatcher)
        }
        pending = true
    }
}


export default watcher

// watch 监听触发流程 --类似updated，都是在更新完视图后调用函数，只不过有存储原来的值（在初始化和每次更新的时候）
/**
 * 1. 赋值修改数据
 * 2. 触发dep -> watcher.update
 * 3. 触发 run里面的回调函数
 */